package com.sdy.kzyy.biz.service.impl;

import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.sdy.common.model.BizException;
import com.sdy.common.model.Response;
import com.sdy.common.utils.Assert;
import com.sdy.common.utils.EncodeUtil;
import com.sdy.common.utils.IdCardUtil;
import com.sdy.common.utils.RandomUtil;
import com.sdy.kzyy.biz.constants.Constants;
import com.sdy.kzyy.biz.dto.BookingSituationDTO;
import com.sdy.kzyy.biz.dto.SweepCodeStaffDTO;
import com.sdy.kzyy.biz.dto.UserDTO;
import com.sdy.kzyy.biz.model.Store;
import com.sdy.kzyy.biz.vo.UserVo;
import com.sdy.kzyy.biz.model.OrderRecord;
import com.sdy.kzyy.biz.model.StoreInventory;
import com.sdy.kzyy.biz.model.User;
import com.sdy.kzyy.biz.mapper.UserMapper;
import com.sdy.kzyy.biz.service.*;
import com.sdy.kzyy.biz.utils.ParamChecker;
import com.sdy.kzyy.biz.utils.QrCodeUtil;
import com.sdy.kzyy.biz.utils.TimeChecker;
import com.sdy.mvc.service.impl.BaseServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.interceptor.TransactionAspectSupport;

import java.awt.*;
import java.io.File;
import java.io.FileOutputStream;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author zzq
 * @since 2020-03-03
 */
@Slf4j
@Service
public class UserServiceImpl extends BaseServiceImpl<User> implements UserService {
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private StoreInventoryService storeInventoryService;
    @Autowired
    private OrderRecordService orderRecordService;
    @Autowired
    private PersonalInformationService personalInformationService;
    @Autowired
    private KeyValueConfigurationsService keyValueConfigurationsService;
    @Autowired
    private StoreService storeService;

    public static Map<String, AtomicInteger> restMap = new ConcurrentHashMap<>();

    @Override
    public User getUserByWxid(String wxid) {
        return lambdaQuery().eq(User::getWxid, wxid).one();
    }

    @Override
    public User xGetOrCreateUserByWxid(String wxid) {
        User user = getUserByWxid(wxid);
        if (user == null) {
            user = createUser(wxid);
        }
        return user;
    }

    @Override
    public User createUser(String wxid) {
        User user = new User();
        user.setWxid(wxid);
        user.setQrCheck(2);
        user.setCreateTime(new Date());
        save(user);
        return user;
    }

    @Override
    public void updateUser(UserDTO userDTO) {
        personalInformationService.saveOrUpdateInformation(userDTO);
    }

    @Override
    public void applyKz(UserDTO userDTO) throws BizException, InterruptedException{
        // 判断是否在预约时间内
        Assert.notTrue((TimeChecker.check(keyValueConfigurationsService.getAppointmentTime(),
                keyValueConfigurationsService.getAppointmentWeek())), "不在可预约时间范围内!");
        // 1.检查表单信息
        checkApplyFormParam(userDTO);
        // 2.检测当前sfz是否符合预约条件
        checkApplySuccessTime(userDTO);
        // 初始化各个药店口罩数量能产生的订单数
        init(userDTO.getOrderStoreCode());

        Integer rest = restMap.get(userDTO.getOrderStoreCode()).getAndDecrement();
        if(rest > 0) {
            try{
                // 3.保存用户信息
                updateUser(userDTO);
                // 4.更新口罩数据
                storeInventoryService.updateStoreKzCount(userDTO.getOrderStoreCode());
                // 5.生成订单
                createOrder(userDTO);
            }catch (Exception e) {
                TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
                restMap.get(userDTO.getOrderStoreCode()).getAndIncrement();
                Assert.isTrue(true,"预约失败!");
            }
        }else {
            restMap.get(userDTO.getOrderStoreCode()).getAndIncrement();
            Assert.isTrue(true,"该店口罩已被预约完!");
        }
    }

    public void createOrder(UserDTO userDTO) throws BizException{
        // 生成订单
        OrderRecord orderRecord = new OrderRecord();
        orderRecord.setOrderId(getOrderId());
        orderRecord.setOrderTime(new Date());
        orderRecord.setOrderCard(userDTO.getCardid());
        orderRecord.setOrderStoreCode(userDTO.getOrderStoreCode());
        orderRecord.setReceiveStatus(1);
        orderRecord.setOrderStatus(2);
        orderRecord.setUserId(userDTO.getUserId());
        // 订单二维码
        createOrderRecorde(orderRecord, userDTO);
    }

    /**
     * 订单二维码
     */
    private OrderRecord createOrderRecorde(OrderRecord orderRecord, UserDTO userDTO) throws BizException{
        // 创建用户QR文件夹
        String folderDir = Constants.QRCODE_PATH + "/" + orderRecord.getOrderCard();
        File dir = new File(folderDir);
        if (!dir.exists()) {
            dir.mkdirs();
        }
        String filePath = folderDir + "/" + System.currentTimeMillis() + "_" + RandomUtil.produceNumber(5) + "." + Constants.QRCODE_FORMAT;
        // 创建二维码
        String tk = "0001" + UUID.randomUUID().toString().replace("-", "")
                + EncodeUtil.md5(System.currentTimeMillis() + "").substring(0, 8);
        orderRecord.setQrcodePath(filePath);
        orderRecord.setToken(tk);
/*        synchronized(this) {
            // 新建订单前再次判断是否今天已经预约成功过
            checkApplySuccessTime(userDTO);
            Assert.isTrue(!orderRecordService.insertNewOrder(orderRecord), "订单新建失败!");
        }

        //防止单个身份证当天出现多个订单的情况
        Assert.isTrue((orderRecordService.getOnedayRecordInfoByCardId(orderRecord.getOrderCard()).size() != 1), "警告，请规范预约!");*/
        Assert.isTrue(!orderRecordService.insertNewOrder(orderRecord), "订单新建失败!");
        log.info(orderRecord.getOrderCard() + "的用户的订单" + orderRecord.getOrderId()+ "新建成功");
        String address = keyValueConfigurationsService.getQrcodeAddress() + "?tk=" + orderRecord.getToken();
        createQrCode(Color.black, address, filePath);
        return orderRecord;
    }

    /**
     * 创建二维码
     */
    private void createQrCode(Color color, String content, String filePath) {
        log.info("生成二维码，内容：" + content);
        try (FileOutputStream fos = new FileOutputStream(filePath)) {
            QrCodeUtil.createQrCode(
                    Constants.QRCODE_FORMAT,
                    fos,
                    content,
                    color,
                    Color.WHITE,
                    null,//ImageIO.read(new ClassPathResource("qrlogo.jpg").getInputStream()),
                    Constants.QRCODE_SIZE);
        } catch (Exception e) {
            String err = "二维码生成失败，请稍后再试。";
            log.error(err, e);
            File f = new File(filePath);
            if (f.exists()) {
                f.delete();
            }
            throw new RuntimeException(err);
        }
    }

    /**
     * 初始化各个药店口罩数量
     * @param storeCode 药店编码
     */
    private void init(String storeCode) {
        if (!restMap.containsKey(storeCode)) {
            synchronized (UserServiceImpl.class) {
                if (!restMap.containsKey(storeCode)) {
                    StoreInventory storeInventory = storeInventoryService.getStoreDate(storeCode);
                    restMap.put(storeCode, new AtomicInteger((storeInventory.getKzCount()/storeInventory.getOnceCount())));
                }
            }
        }
    }

    /**
     * 验证表单数据
     * @param userDTO
     * @throws BizException
     */
    private void checkApplyFormParam(UserDTO userDTO) throws BizException {
        Assert.isTrue(!ParamChecker.checkParamRequired(userDTO.getName()), "参数<姓名>不能为空。");
        Assert.isTrue(!ParamChecker.checkParamRequired(userDTO.getCardid()), "参数<身份证号>不能为空。");
        Assert.isTrue(!IdCardUtil.IDCardValidate(userDTO.getCardid()).equals("true"), "身份证号格式错误。");
        Assert.isTrue(!ParamChecker.checkParamRequired(userDTO.getPhone()), "参数<手机号>不能为空。");
        Assert.isTrue(!userDTO.getPhone().matches(com.sdy.common.constant.Constants.REGEX_PHONE), "手机号格式错误。");
        Assert.isTrue(!ParamChecker.checkParamRequired(userDTO.getOrderStoreCode()), "参数<药店编码>不能为空。");
    }

    public void checkApplySuccessTime(UserDTO userDTO) throws BizException{
        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");//设置日期格式
        // 通过身份证查询最新成功订单时间
        List<OrderRecord> recordInfoByCardId = orderRecordService.getRecordInfoByCardId(userDTO.getCardid());
        if(recordInfoByCardId.size() != 0) {
            String newTime = df.format(recordInfoByCardId.get(0).getOrderTime());
            String nowTime = df.format(new Date());
            long newTimeStamp = dateToStamp(newTime);
            long nowTimeStamp = dateToStamp(nowTime);
            long timeInterval = Long.parseLong(keyValueConfigurationsService.getOrderInitiationInterval());
            boolean can = (nowTimeStamp - newTimeStamp) > timeInterval * 24 * 60 * 60 * 1000L;
            String msg = "距离上次预约时间未超过" + timeInterval +"天";
            Assert.isTrue(!can, msg);
        }
    }

    public long dateToStamp(String s) {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
        Date date = new Date();
        try {
            date = simpleDateFormat.parse(s);
        } catch (Exception e) {
            return -1;
        }
        long time = date.getTime();
        return time;
    }

    public static String getOrderId() {
        String orderId = null;
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS");
        int random = new Random().nextInt(100000000);
        orderId = sdf.format(new Date()) + "_" + random + "0000000000";
        return orderId.substring(0, 30);
    }

    @Override
    public Response authority(Integer userId, UserVo userVo) {
        User user = new User();
        user.setManagerName(userVo.getManagerName());
        user.setManagerPhone(userVo.getManagerPhone());
        user.setStoreCode(userVo.getStoreCode());
        user.setUpdateTime(new Date());
        if("0001b2811ada04344eabbf92c210322363fe06984b74".equals(userVo.getAuthorityQr())){
            user.setQrCheck(1);
            update(user, new UpdateWrapper<User>().eq("id", userId));
        }else if("00018eef5c8d7df54c05b6da1b673dac86fb8f05dc0c".equals(userVo.getAuthorityQr())) {
            user.setQrCheck(2);
            update(user, new UpdateWrapper<User>().eq("id", userId));
        }else {
            return Response.error("授权失败");
        }
        return Response.success("成功");
    }

    @Override
    public Boolean cancelPower(BookingSituationDTO bookingSituationDTO) throws BizException {
        Assert.isNull(bookingSituationDTO.getId(), "传入数据存在问题!");
        Assert.isNull(bookingSituationDTO.getState(), "传入数据存在问题!");
        Integer state = 0;
        Integer cancel = 0;
        if (bookingSituationDTO.getState() == 1) {
            state = 2;
            cancel = 1;
        } else if (bookingSituationDTO.getState() == 2) {
            state = 1;
            User byId = getById(bookingSituationDTO.getId());
            String storeCode = byId.getStoreCode();
            Store one = storeService.getOne(Wrappers.<Store>lambdaQuery().eq(Store::getStoreCode, storeCode));
            Integer storeStatus = one.getStoreStatus();
            Assert.isTrue(storeStatus == 2, "当前药店被禁用,无法正常恢复人员权限!");
        }
        boolean update = update(Wrappers.<User>lambdaUpdate()
                .eq(User::getId, bookingSituationDTO.getId())
                .set(User::getCancelReason, cancel)
                .set(User::getQrCheck, state)
                .set(User::getUpdateTime, new Date())
        );
        Assert.notTrue(update, "取消权限失败!");
        return true;
    }

    @Override
    public List<SweepCodeStaffDTO> getSweepPage(String storeCode, String name, Integer state, Page<User> page) {
        return userMapper.getSweepPage(storeCode, name, state, page);
    }

    @Override
    public List<SweepCodeStaffDTO> getSweepPage(String storeCode, String name, Integer state) {
        return userMapper.getSweepPage(storeCode,name,state);
    }

    @Override
    public Integer getSweepSize(String storeCode,String name, Integer state) {
        return userMapper.getSweepSize(storeCode,name, state);
    }

}
